const express = require('express')
const logger = require('morgan')
const { StatusCodes } = require('http-status-codes')
const fs = require('fs-extra')
const path = require('path')
const { log } = require('console')
const router = express.Router()
const PUBLIC_DIR = path.relative(__dirname, 'public')
const TEMP_DIR = path.relative(__dirname, 'temp')
const CHUNK_SIZE = 100 * 1024 * 1024
//存放上传好并合并好文件
fs.ensureDirSync(PUBLIC_DIR)
//存放分片的文件
fs.emptyDirSync(TEMP_DIR)

router.post('/upload/:filename', async (req, res, next) => {
    const { filename } = req.params
    const { chunkFileName } = req.query;
    const start = isNaN(req.query.start) ? 0 : parseInt(req.query.start, 10);
    //创建用户保存此文件的分片的目录
    const chunkDir = path.resolve(TEMP_DIR, filename)
    //分片的文件路径
    const chunkFilePath = path.resolve(chunkDir, chunkFileName)
    //先确保分片目录存在
    await fs.ensureDir(chunkDir)
    //创建此文件的可写流,可以指定写入的起始位置
    const ws = fs.createWriteStream(chunkFilePath, { start, flage: 'a' })
    //后面会事先暂停操作，如果客户端点击了暂停按钮，会取消上传的操作
    //aborted事件，关闭可定流
    req.on('aborted', () => { ws.close() })
    //使用管道的方式把请求中的请求体流数据写入到文件中
    try {
        await pipeStream(req, ws)
        res.json({
            success: true
        })
    } catch (error) {
        next(error)
    }

})
router.get('/merge/:filename', async (req, res, next) => {
    const { filename } = req.params
    try {
        await mergeChunks(filename)
        res.json({
            success: true
        })
    } catch (error) {
        next(error)
    }
})
router.get('/verify/:filename', async (req, res, next) => {
    const { filename } = req.params;
    //先获取文件在服务器的路径
    const filePath = path.resolve(PUBLIC_DIR, filename);
    //判断文件在服务器端是否存在
    const isExist = await fs.pathExists(filePath)
    //如果已经存在了，则直接返回不需要上传了
    if (isExist) {
        return res.json({ success: true, needUpload: false })
    }
    const chunksDir = path.resolve(TEMP_DIR, filename);
    const exist = await fs.pathExists(chunksDir)
    //存放已经上传的分片的对象数组
    let uploadedChunkList = []
    if (exist) {
        //读取临时目录里面的所有的分片对应的文件
        const chunkFileNames = await fs.readdir(chunksDir)
        //读取每个分片文件的文件信息，主要是他的文件大小，表示已经上传的文件大小
        uploadedChunkList = await Promise.all(chunkFileNames.map(async function (chunkFileName) {
            const { size } = await fs.stat(path.resolve(chunksDir, chunkFileName));
            return { chunkFileName, size }
        }))
    }
    //如果没有此文件，则意味着服务器还需要你上传此文件
    //返回，上传尚未完成，但是已经上传了一部分了，我把已经上传的分片名，以及分片的大小给客户端
    //客户端可以只上传分片剩下的数据就可以了
    res.json({
        success: true,
        needUpload: true,
        uploadedChunkList
    })

})
async function mergeChunks(filename) {
    const mergedFilePath = path.resolve(PUBLIC_DIR, filename)
    const chunkDir = path.resolve(TEMP_DIR, filename);
    const chunkFiles = await fs.readdir(chunkDir)
    //对分片进行升序排序
    chunkFiles.sort((a, b) => Number(a.split('-')[1]) - Number(b.split('-')[1]));
    try {
        //为了提高性能，我们在这里可以实现一个并行写入
        const pipes = chunkFiles.map((chunkFile, index) => {
            return pipeStream(
                fs.createReadStream(path.resolve(chunkDir, chunkFile), { autoClose: true }),
                fs.createWriteStream(mergedFilePath, { start: index * CHUNK_SIZE })
            )
        })
        console.log(pipes);

        //并发把每个分片的数据写入到目标文件中
        await Promise.all(pipes)
        //删除分片的文件和文件夹
        await fs.rmdir(chunkDir, { recursive: true })
    } catch (error) {
        // next(error)
    }
}
function pipeStream(rs, ws) {
    return new Promise((resolve, reject) => {
        //把可读流中的数据写入可写流中
        rs.pipe(ws).on('finish', resolve).on('error', reject)
    });
}

module.exports = router

